﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using Tools.Debugging;
using System.Security.Authentication;
using repliback4.Net.Protocol.SASLMechanisms;
using repliback4.Misc.Queue;
using repliback4.Net.Protocol.Messages;
using repliback4.Net.Identification;
using System.Threading;

namespace repliback4.Net.ServerSide {

    public class ServerSettings {
        #region Fields
        protected IPEndPoint localEndpoint = new IPEndPoint( IPAddress.Any, 5566 );
        protected Host host = "localhost";
        #endregion

        #region Getters / Setters
        public int Port {
            get { return this.localEndpoint.Port; }
            set { this.localEndpoint.Port = value; }
        }
        public IPAddress Address {
            set { this.localEndpoint.Address = value; }
            get { return this.localEndpoint.Address; }
        }
        public IPEndPoint LocalEndpoint {
            get { return this.localEndpoint; }
        }

        public Host HostName { 
            get { return this.host; }
        }
        static public ServerSettings Default {
            get { return new ServerSettings( ); }
        }
        #endregion

    }


    public struct QueueQueryItem {
        public Client Client;
        public InfoQuery Query;
    }


    public enum ServerQueueDirection { FromClient, ToClient }
    public class Server {

        protected ServerSettings settings;
        public ServerSettings Settings { get { return this.settings; } }
        protected TcpListener listener;

        public MechanismsRegistry authMechanism = MechanismsRegistry.Default;

        //protected Queue<IRequest> requestQueue = new Queue<IRequest>( );

        protected QueueManager<QueueQueryItem, ServerQueueDirection> queue;
        public QueueManager<QueueQueryItem, ServerQueueDirection> Queue { get { return this.queue; } }

        protected List<Client> clientList = new List<Client>( );

        
        #region Constructors
        public Server ( ServerSettings settings ) {
            Logger.Created( this);
            this.settings = settings;
            this.InitQueue( );
        }

        public Server ( ) {
            Logger.Created( this );
            this.settings = ServerSettings.Default;
            this.InitQueue( );
        }
        #endregion

        protected void InitQueue ( ) {
            this.queue = new QueueManager<QueueQueryItem, ServerQueueDirection>( );
            this.queue.CreateQueue( ServerQueueDirection.FromClient );
            //this.queue.CreateQueue( ServerQueueDirection.FromClient );
            this.queue.AddOnItemInQueueHandler( ServerQueueDirection.FromClient, new AutoQueue<QueueQueryItem>.OnItemInQueueHandler( this.handleQueryFromClient ) );
        }

        private void handleQueryFromClient ( QueueQueryItem item ) {
            Logger.Debug( this, "handling query {0} from {1}", item.Query.TagName, item.Client.ClientInfo.ToString( ) );
        }

        public void Start ( ) {
            try {
                Logger.Debug( this, "Server starting..." );
                this.listener = new TcpListener( this.settings.LocalEndpoint );
                this.listener.Start( );
                this.listenerStart( this.listener );
                Logger.Debug( this, "Server started." );
            } catch ( SocketException e ) {
                Logger.Debug( this, String.Format( "Server start failed. [{0}]", e.SocketErrorCode ) );
            }
        }

        protected void listenerStart ( TcpListener listener ) {
            Logger.Debug( this, "Setting connection callback..." );
            listener.BeginAcceptTcpClient( new AsyncCallback( this.acceptClient ), this.listener );
            Logger.Debug( this, "Setting connection callback done." );
        }

        protected void acceptClient ( IAsyncResult result ) {
            try {
                Thread.CurrentThread.Name = "client";
                Logger.Debug( this, "client accepted" );
                TcpListener listener = ( TcpListener ) result.AsyncState;
                TcpClient tcpClient = listener.EndAcceptTcpClient( result );
                Client client = new Client( this, tcpClient );
                this.clientList.Add( client );
                client.Start( );
            } catch ( SocketException e ) {
                Logger.Exception( this, String.Format( "acceptClient SocketException: [{0}] :: [{1}]", e.SocketErrorCode, e.Message ),e );
            } catch ( AuthenticationException e ) {
                Logger.Exception( this, String.Format( "acceptClient AuthenticationException: [{0}], [{1}]", e.Message, e.InnerException.Message ),e );
            } catch ( Exception e ) {
                Logger.Exception( this, String.Format( "acceptClient Exception: [{0}]", e.Message ), e );
            } finally {
                this.listenerStart( listener );
            }
        }

        internal void Disconnected ( Client client ) {
            this.clientList.Remove( client );
            client.Dispose( );
        }
    }
}
